Release 10.1A: OpenEdge Development:
Progress 4GL Handbook


Using widget pools

Deleting individual dynamic objects is a big responsibility, and a serious nuisance as well. Widget pools are designed to help you make your use of dynamic objects much simpler and more reliable.

A widget pool provides a means of treating objects you create as a set. When you delete the pool, all the objects you created in it go away together. The simplest way to group objects using widget pools is to associate all the objects in a single procedure with a widget pool. In fact, the template procedures used by the AppBuilder for windows (and for visual SmartObjects, such as SmartWindows and SmartDataViewers) help you do this by having a CREATE WIDGET-POOL statement in the template’s definitions section:

/* Create an unnamed pool to store all the widgets created  
     by this procedure. This is a good default which assures 
     that this procedure's triggers and internal procedures  
     will execute in this procedure's storage, and that proper 
     cleanup will occur on deletion of the procedure. */ 
CREATE WIDGET-POOL. 

This means that, by default, all the dynamic objects you create that go into the window are deleted when the window is closed and its procedure deleted. This includes not just visual objects but dynamic buffers, queries, and so forth.

Remember that a simple CREATE WIDGET-POOL statement creates an unnamed widget pool. This pool automatically goes away when its procedure terminates. This might not always be the behavior you want. In fact, by passing the handles to dynamic objects around, you can easily wind up with a handle whose scope exceeds the lifetime of the dynamic object it points to. In this case, the value of the handle can become invalid.

Here’s a simple example of how this can happen. The h-MakeBuffer.p procedure has an internal procedure called getBuffer that creates a dynamic buffer and returns it to the caller. The CREATE WIDGET-POOL statement puts all such buffers into an unnamed widget pool for that procedure:

/* h-MakeBuffer.p */ 
CREATE WIDGET-POOL.  /* Unnamed widget pool! */ 
PROCEDURE getBuffer: 
     DEFINE INPUT  PARAMETER cTable AS CHARACTER  NO-UNDO. 
     EFINE OUTPUT PARAMETER hBuffer AS HANDLE     NO-UNDO. 
     /* The buffer will be allocated to the unnamed widget pool. */ 
     CREATE BUFFER hBuffer FOR TABLE cTable. 
END PROCEDURE. 

Another procedure, h-RunMakeBuff.p, runs h-MakeBuffer.p PERSISTENT and then runs getBuffer:

/* h-RunMakeBuff.p */ 
DEFINE VARIABLE hProc  AS HANDLE     NO-UNDO. 
DEFINE VARIABLE hMyBuf AS HANDLE     NO-UNDO. 
RUN h-MakeBuffer.p PERSISTENT SET hProc. 
RUN getBuffer IN hProc (INPUT "Customer", OUTPUT hMyBuf). 
MESSAGE "While MakeBuffer is alive, my buffer is "  
    hMyBuf:NAME VIEW-AS ALERT-BOX. 
DELETE PROCEDURE hProc. 
MESSAGE " After I delete it, I get nasty errors" 
    hMyBuf:NAME VIEW-AS ALERT-BOX. 

The first message statement, as expected, correctly displays the name of the dynamic buffer that was returned, as shown in Figure 21–6.

Figure 21–6: Result of widget pool example

What happens, though, when the procedure has deleted its instance of h-MakeBuffer.p?

The unnamed widget pool in that instance of h-MakeBuffer.p goes away, and the dynamic buffer that hMyBuf points to goes away with it. But the hMyBuf variable is still very much alive, and in fact the value of the handle that it holds hasn’t changed. However, that handle value doesn’t point to anything anymore, so you get a string of errors, as shown in Figure 21–7.

Figure 21–7: Errors for unnamed widget pool example

This sequence of errors is the bane of any dynamic programmer’s existence. It is the most likely consequence of failing to make sure that all your dynamic objects live exactly as long as they need to, but no longer.

Using named widget pools

The solution to this is to give names to widget pools that hold objects that might outlive the procedure that created them. In addition to giving the pool a name, you must also define it to be PERSISTENT. You can then create dynamic objects explicitly in that pool:

/* h-MakeBuffer.p */ 
CREATE WIDGET-POOL "MakeBuffPool" PERSISTENT. 
PROCEDURE getBuffer: 
    DEFINE INPUT  PARAMETER cTable AS CHARACTER  NO-UNDO. 
    DEFINE OUTPUT PARAMETER hBuffer AS HANDLE    NO-UNDO. 
    /* The buffer will be allocated to the unnamed widget pool. */ 
    CREATE BUFFER hBuffer FOR TABLE cTable IN WIDGET-POOL "MakeBuffPool". 
END PROCEDURE. 

The persistent widget pool becomes another dynamic object that your application has to take responsibility for managing. You must delete it when you’re finally done with, or all the objects in it will sit there in memory until your session ends. Here the calling procedure cleans up after it is done using the buffer handle:

/* … end of h-RunMakeBuff.p … */ 
DELETE PROCEDURE hProc. 
MESSAGE " No more errors, the buffer is still" 
    hMyBuf:NAME VIEW-AS ALERT-BOX. 
DELETE WIDGET-POOL "MakeBuffPool". 

When you run the procedure, the errors go away as shown in Figure 21–8, because the handle is valid until you delete the pool.

Figure 21–8: Result of named widget pool example

Remember that all these steps are important:

  1. You must give the widget pool a name if you want to allocate objects to it specifically or if you want it to outlive the procedure that creates it.
  2. The widget pool name is a character expression. So, if you are using a literal string rather than a variable for the name, you must remember to put it in quotes both where you create it and wherever you reference it. This is different from other CREATE statements, where you normally specify a handle variable as a target for the create and where the dynamic object you create doesn’t really have a name the same way that static objects do.
  3. You must also make a named pool PERSISTENT if you want it to outlive the procedure that creates it.
  4. You must remember to delete the widget pool when you are done using it, just as you delete individual dynamic objects that aren’t in a specific widget pool when you are done with them.

You might create a named widget pool that was not persistent simply to put different dynamic objects in different pools within a single procedure, so that you could delete one widget pool within the procedure without deleting objects in some other widget pool. In this case, all of the pools that haven’t been specifically deleted during the execution of the procedure are deleted when the procedure is deleted.

A widget pool cannot be created as PERSISTENT without giving it a name.

When you create a persistent named pool, its name effectively becomes global to the session. Any procedure running anywhere in the session can delete it. You cannot, however, access named widget pools through the SESSION handle as you can with some other kinds of objects, such as windows and procedures.


Copyright © 2005 Progress Software Corporation
www.progress.com
Voice: (781) 280-4000
Fax: (781) 280-4095